
// <copyright file="AbstractMetricsEnabledComponentController.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The abstract metrics enabled component controller.
// </summary>

namespace SubhadraSolutions.Sharp.Utils
{
    using System;
    using System.Threading;

    using SubhadraSolutions.Sharp.Utils.Contracts;
    using SubhadraSolutions.Sharp.Utils.Diagnostics;
    using SubhadraSolutions.Sharp.Utils.Diagnostics.Performance;

    // [PerfCounterCategory("Component Controllers")]

    /// <summary>
    /// The abstract metrics enabled component controller.
    /// </summary>
    public abstract class AbstractMetricsEnabledComponentController : IMetricsEnabledComponentController
    {
        #region Fields

        /// <summary>
        /// The busy started at
        /// </summary>
        private long busyStartedAt;

        /// <summary>
        /// The idle started at.
        /// </summary>
        private long idleStartedAt;

        /// <summary>
        /// The number of threads being served.
        /// </summary>
        private long numberOfThreadsBeingServed;

        /// <summary>
        /// The pause started at.
        /// </summary>
        private long pauseStartedAt;

        /// <summary>
        /// The stop started at.
        /// </summary>
        private long stopStartedAt;

        /// <summary>
        /// The total ticks in busy.
        /// </summary>
        private long totalTicksInBusy;

        /// <summary>
        /// The total ticks in idle.
        /// </summary>
        private long totalTicksInIdle;

        /// <summary>
        /// The total ticks in paused.
        /// </summary>
        private long totalTicksInPaused;

        /// <summary>
        /// The total ticks in stopped.
        /// </summary>
        private long totalTicksInStopped;

        #endregion Fields

        #region Constructors and Finalizers

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractMetricsEnabledComponentController"/> class.
        /// </summary>
        /// <param name="canPause">
        /// The can pause.
        /// </param>
        protected AbstractMetricsEnabledComponentController(bool canPause)
        {
            this.State = ControllableComponentState.NotStarted;
            this.SupportsPauseAndResume = canPause;
        }

        #endregion Constructors and Finalizers

        #region Public Properties

        /// <summary>
        /// Gets the busy percentage.
        /// </summary>
        [PerfCounter]
        public double BusyPercentage
        {
            get
            {
                long ticks = this.TimeSpentInBusy.Ticks;
                long totalTicks = this.TimeSpentInIdle.Ticks + ticks;
                if (totalTicks == 0)
                {
                    return 0;
                }

                return 100 * ticks / (double)totalTicks;
            }
        }

        /// <summary>
        /// Gets the idle percentage.
        /// </summary>
        [PerfCounter]
        public double IdlePercentage
        {
            get
            {
                long ticks = this.TimeSpentInIdle.Ticks;
                long totalTicks = this.TimeSpentInBusy.Ticks + ticks;
                if (totalTicks == 0)
                {
                    return 0;
                }

                return 100 * ticks / (double)totalTicks;
            }
        }

        /// <summary>
        /// Gets the number of threads being served.
        /// </summary>
        [PerfCounter]
        public long NumberOfThreadsBeingServed
        {
            get
            {
                return Interlocked.Read(ref this.numberOfThreadsBeingServed);
            }
        }

        /// <summary>
        /// Gets the state.
        /// </summary>
        public ControllableComponentState State { get; private set; }

        /// <summary>
        /// Gets a value indicating whether supports pause and resume.
        /// </summary>
        public bool SupportsPauseAndResume { get; private set; }

        /// <summary>
        /// Gets the time spent in busy.
        /// </summary>
        [PerfCounter]
        public TimeSpan TimeSpentInBusy
        {
            get
            {
                long ticks = Interlocked.Read(ref this.totalTicksInBusy);
                if (this.State == ControllableComponentState.Busy)
                {
                    ticks += SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.busyStartedAt);
                }

                return TimeSpan.FromTicks(ticks);
            }
        }

        /// <summary>
        /// Gets the time spent in idle.
        /// </summary>
        [PerfCounter]
        public TimeSpan TimeSpentInIdle
        {
            get
            {
                long ticks = Interlocked.Read(ref this.totalTicksInIdle);
                if (this.State == ControllableComponentState.Idle)
                {
                    ticks += SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.idleStartedAt);
                }

                return TimeSpan.FromTicks(ticks);
            }
        }

        /// <summary>
        /// Gets the time spent in paused.
        /// </summary>
        [PerfCounter]
        public TimeSpan TimeSpentInPaused
        {
            get
            {
                long ticks = Interlocked.Read(ref this.totalTicksInPaused);
                if (this.State == ControllableComponentState.Paused)
                {
                    ticks += SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.pauseStartedAt);
                }

                return TimeSpan.FromTicks(ticks);
            }
        }

        /// <summary>
        /// Gets the time spent in stopped.
        /// </summary>
        [PerfCounter]
        public TimeSpan TimeSpentInStopped
        {
            get
            {
                long ticks = Interlocked.Read(ref this.totalTicksInStopped);
                if (this.State == ControllableComponentState.Stopped)
                {
                    ticks += SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.stopStartedAt);
                }

                return TimeSpan.FromTicks(ticks);
            }
        }

        #endregion Public Properties

        #region Public Methods and Operators

        /// <summary>
        /// The pause.
        /// </summary>
        public void Pause()
        {
            if (this.SupportsPauseAndResume)
            {
                if (this.SupportsPauseAndResume && this.State == ControllableComponentState.Idle || this.SupportsPauseAndResume && this.State == ControllableComponentState.Busy)
                {
                    this.State = ControllableComponentState.Pausing;
                    this.pause();
                    this.State = ControllableComponentState.Paused;
                    this.pauseStartedAt = SharedStopWatch.ElapsedTicks;
                }
            }
        }

        /// <summary>
        /// The resume.
        /// </summary>
        public void Resume()
        {
            if (this.SupportsPauseAndResume)
            {
                if (this.State == ControllableComponentState.Paused)
                {
                    this.State = ControllableComponentState.Resuming;
                    this.resume();
                    this.State = ControllableComponentState.Idle;
                    Interlocked.Add(ref this.totalTicksInPaused, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.pauseStartedAt));
                    this.idleStartedAt = SharedStopWatch.ElapsedTicks;
                }
            }
        }

        /// <summary>
        /// The start.
        /// </summary>
        public void Start()
        {
            if (this.State == ControllableComponentState.NotStarted || this.State == ControllableComponentState.Stopped)
            {
                if (this.State == ControllableComponentState.Stopped)
                {
                    Interlocked.Add(ref this.totalTicksInStopped, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.stopStartedAt));
                }

                this.State = ControllableComponentState.Starting;
                this.start();
                this.State = ControllableComponentState.Idle;
                this.idleStartedAt = SharedStopWatch.ElapsedTicks;
            }
        }

        /// <summary>
        /// The started doing something.
        /// </summary>
        public void StartedDoingSomething()
        {
            if (Interlocked.Increment(ref this.numberOfThreadsBeingServed) == 1)
            {
                Interlocked.Add(ref this.totalTicksInIdle, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.idleStartedAt));
                this.busyStartedAt = SharedStopWatch.ElapsedTicks;
                this.State = ControllableComponentState.Busy;
            }
        }

        /// <summary>
        /// The stop.
        /// </summary>
        public void Stop()
        {
            if (this.State == ControllableComponentState.Idle || this.SupportsPauseAndResume && this.State == ControllableComponentState.Busy)
            {
                if (this.State == ControllableComponentState.Idle)
                {
                    Interlocked.Add(ref this.totalTicksInIdle, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.idleStartedAt));
                }
                else
                {
                    Interlocked.Add(ref this.totalTicksInBusy, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.busyStartedAt));
                }

                this.State = ControllableComponentState.Stopping;
                this.stop();
                this.State = ControllableComponentState.Stopped;
                this.stopStartedAt = SharedStopWatch.ElapsedTicks;
            }
        }

        /// <summary>
        /// The stopped doing something.
        /// </summary>
        public void StoppedDoingSomething()
        {
            if (Interlocked.Decrement(ref this.numberOfThreadsBeingServed) == 0)
            {
                Interlocked.Add(ref this.totalTicksInBusy, SharedStopWatch.ElapsedTicks - Interlocked.Read(ref this.busyStartedAt));
                this.State = ControllableComponentState.Idle;
                this.idleStartedAt = SharedStopWatch.ElapsedTicks;
            }
        }

        #endregion Public Methods and Operators

        #region Methods

        /// <summary>
        /// The pause.
        /// </summary>
        protected virtual void pause()
        {
        }

        /// <summary>
        /// The resume.
        /// </summary>
        protected virtual void resume()
        {
        }

        /// <summary>
        /// The start.
        /// </summary>
        protected abstract void start();

        /// <summary>
        /// The stop.
        /// </summary>
        protected abstract void stop();

        #endregion Methods
    }
}